﻿using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace WebLib.FileUpload
{
    #region LogonType, LogonProvider

    public enum LogonType
    {
        Logon32LogonInteractive = 2,
        Logon32LogonNetwork = 3,
        Logon32LogonBatch = 4,
        Logon32LogonService = 5,
        Logon32LogonUnlock = 7,
        Logon32LogonNetworkCleartext = 8,
        Logon32LogonNewCredentials = 9
    }

    public enum LogonProvider
    {
        Logon32ProviderDefault = 0,
        Logon32ProviderWinnt35 = 1,
        Logon32ProviderWinnt40 = 2,
        Logon32ProviderWinnt50 = 3
    }

    #endregion

    public class ImpersonateService : IDisposable
    {
        private WindowsImpersonationContext _impersonationContext;

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern int LogonUser(String lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool RevertToSelf();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);

        public bool ImpersonateValidUser(string userName, string domain, string password)
        {
            var token = IntPtr.Zero;
            var tokenDuplicate = IntPtr.Zero;

            if (RevertToSelf())
            {
                //if (LogonUser(userName, domain, password, (int)LogonType.Logon32LogonNetwork, (int)LogonProvider.Logon32ProviderDefault, ref token) != 0)
                if (LogonUser(userName, domain, password, (int)LogonType.Logon32LogonNetwork, (int)LogonProvider.Logon32ProviderDefault, ref token) != 0)
                {
                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                    {
                        var tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        _impersonationContext = tempWindowsIdentity.Impersonate();
                        if (_impersonationContext != null)
                        {
                            CloseHandle(token);
                            CloseHandle(tokenDuplicate);
                            return true;
                        }
                    }
                }
            }

            if (token != IntPtr.Zero) CloseHandle(token);
            if (tokenDuplicate != IntPtr.Zero) CloseHandle(tokenDuplicate);
            return false;
        }

        public void UndoImpersonation()
        {
            if (_impersonationContext != null)
                _impersonationContext.Undo();
        }

        #region IDisposable 멤버

        public void Dispose()
        {
            UndoImpersonation();
        }

        #endregion
    }
}
